#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <scsi/sg.h>
#include <sys/ioctl.h>
#include <string.h>

#define SENSE_LEN       255
#define BLOCK_LEN       32

struct st_drvver {
	unsigned int major;
	unsigned int minor;
	unsigned int oem;
	unsigned int build;
	unsigned int signature[2];
	unsigned char console_id;
	unsigned char host_no;
	unsigned char reserved0[2];
	unsigned int reserved[3];
};

unsigned char sense_buffer[SENSE_LEN];
unsigned char data_buffer[BLOCK_LEN*256];

void show_drvver(struct st_drvver *dv) {
	printf("major: %#x\n", dv->major);
	printf("minor: %#x\n", dv->minor);
	printf("oem: %#x\n", dv->oem);
	printf("build: %#x\n", dv->build);
	printf("signature: %#x %#x\n", dv->signature[0], dv->signature[1]);
	printf("console_id: %#x\n", dv->console_id);
	printf("host_no: %#x\n", dv->host_no);
	printf("reserved0: %#x %#x\n", dv->reserved0[0], dv->reserved0[1]);
	printf("*(long*)&dv->reserved[0]: %#lx\n", *(long*)&dv->reserved[0]);
	printf("*(long*)&dv->reserved[1]: %#lx\n\n", *(long*)&dv->reserved[1]);
}

void show_sense_buffer(struct sg_io_hdr * hdr) {
	unsigned char * buffer = hdr->sbp;
	int i;
	for (i=0; i<hdr->mx_sb_len; ++i) {
		putchar(buffer[i]);
	}
}

struct sg_io_hdr* init_io_hdr() {

	struct sg_io_hdr * p_scsi_hdr = (struct sg_io_hdr *)malloc(sizeof(struct sg_io_hdr));
	memset(p_scsi_hdr, 0, sizeof(struct sg_io_hdr));
	if (p_scsi_hdr) {
		p_scsi_hdr->interface_id = 'S';
		p_scsi_hdr->flags = SG_FLAG_LUN_INHIBIT;
	}

	return p_scsi_hdr;
}

void destroy_io_hdr(struct sg_io_hdr * p_hdr) {
        if (p_hdr) {
                free(p_hdr);
        }
}

void set_xfer_data(struct sg_io_hdr * p_hdr, void * data, unsigned int length) {
	if (p_hdr) {
		p_hdr->dxferp = data;
		p_hdr->dxfer_len = length;
	}
}

void set_sense_data(struct sg_io_hdr * p_hdr, unsigned char * data,
		unsigned int length) {
	if (p_hdr) {
		p_hdr->sbp = data;
		p_hdr->mx_sb_len = length;
	}
}

int execute_ioctl_cmd(int fd, int page_code, int evpd, struct sg_io_hdr * p_hdr) {
	unsigned char cdb[6];

	/* set the cdb format */
	cdb[0] = 227;
	cdb[1] = 16;
	
	p_hdr->dxfer_direction = SG_DXFER_FROM_DEV;
	p_hdr->cmdp = cdb;
	p_hdr->cmd_len = 6;

	int ret = ioctl(fd, SG_IO, p_hdr);
	if (ret<0) {
		printf("Sending SCSI Command failed.\n");
		close(fd);
		exit(1);
	}

	return p_hdr->status;
}

void test_execute_ioctl_cmd(char * path, int evpd, int page_code) {

	struct sg_io_hdr * p_hdr = init_io_hdr();

	set_xfer_data(p_hdr, data_buffer, BLOCK_LEN*256);
	set_sense_data(p_hdr, sense_buffer, SENSE_LEN);

	struct st_drvver *dv = (struct st_drvver *)data_buffer;
	memset(data_buffer, 0, sizeof(data_buffer));
	printf("============= before ioctl\n");
	show_drvver((struct st_drvver *)dv);

	int status = 0;
	int fd = open(path, O_RDWR);
	if (fd>0) {
		status = execute_ioctl_cmd(fd, page_code, evpd, p_hdr);
		if (status!=0) {
			show_sense_buffer(p_hdr);
		} else{
			printf("============= after ioctl\n");
			show_drvver((struct st_drvver *)dv);
		}
	} else {
		printf("failed to open sg file %s\n", path);
	}
	close(fd);
	destroy_io_hdr(p_hdr);
}

int main(int argc, char * argv[]) {
	test_execute_ioctl_cmd(argv[1], 0, 0);
	return EXIT_SUCCESS;
}
